package proxy

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"github.com/pkg/errors"
	"math/big"
	"net"
	"os"
	"os/exec"
	"runtime"
	"time"
)

var (
	rootCa  *x509.Certificate // CA证书
	rootKey any               // 证书私钥
)

var (
	_rootCa = []byte(`-----BEGIN CERTIFICATE-----
MIID4jCCAsqgAwIBAgIJAKcH8Dna4mnZMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
BAYTAkNOMQswCQYDVQQIDAJCSjEQMA4GA1UEBwwHQmVpSmluZzERMA8GA1UECgwI
UHJveHlQaW4xETAPBgNVBAsMCFByb3h5UGluMRQwEgYDVQQDDAtQcm94eVBpbiBD
QTAeFw0yMzA2MjQxNjA2MDlaFw0zMzA2MjExNjA2MTBaMGgxCzAJBgNVBAYTAkNO
MQswCQYDVQQIDAJCSjEQMA4GA1UEBwwHQmVpSmluZzERMA8GA1UECgwIUHJveHlQ
aW4xETAPBgNVBAsMCFByb3h5UGluMRQwEgYDVQQDDAtQcm94eVBpbiBDQTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMRjfFvFDZS+PsdedUNq0Kn5t7RF
NS0iQrZALr4LJm3UwtatHtMEWBb9ptam8pWezxrZPZ81+qnTcaq/To82yus5hJa4
JRk223YWn5JDd4izH4gcnSomhUQ6Ycrc0v+I7UEaHV+bQsleHEfYi2+E1qF+FBhR
veLSPmz2QORd/U4+gDlOptgNWMQ9OTRHsMoDzb8J4SlcBu+s0dnq2WHOM9boGnfk
2wIgE+16uB23epPoYjex8zYGUswh8gNrIzXsr7i9IGtGf67FQYCWOXfZLeGgy0Q0
/r1lwSmywUkNaZIsiGZHveZsLtW93MWMFw0uneEvHsuQV+e8sdLI/633TGcCAwEA
AaOBjjCBizAdBgNVHQ4EFgQU4YXwKkBDFoZY3D81RM25ECSc2qcwHwYDVR0jBBgw
FoAU4YXwKkBDFoZY3D81RM25ECSc2qcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
Af8EBAMCAaYwEwYDVR0RBAwwCoIIUHJveHlQaW4wEwYDVR0lBAwwCgYIKwYBBQUH
AwEwDQYJKoZIhvcNAQELBQADggEBAAc2s5TwuOdPdl3gYs121sY+HEMyXfsnVTGV
dIlgjf+a0ECir2bcs64udaCIgBjd/vqhShMeqeQ4GJW7Ypb9556L213xjbLj/ZVU
rgZda6oVd4der8YEHXKLxWAGlZQeeKHhw1lN4PYwxxGaf7/wsM7Dil0JLyOBtJaJ
zNRzVzK9UHASDx0qDQVUBbeYzRviVCjxAGBNM/eNlPDX7m//vgCLxQgcxVdoJvMQ
kSVQddo+d8fxnPAVx77dyX0T/ek7PQOsL6d08TVCdvgv50LwE8f9EMhHVv7zjEv2
0ZSaRQ0pvUnc0ClKXIGeMD71eYeeTz7CGjndxy5bdV/wmoo3Yek=
-----END CERTIFICATE-----
`)
	_rootKey = []byte(`-----BEGIN CERTIFICATE-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEY3xbxQ2Uvj7H
XnVDatCp+be0RTUtIkK2QC6+CyZt1MLWrR7TBFgW/abWpvKVns8a2T2fNfqp03Gq
v06PNsrrOYSWuCUZNtt2Fp+SQ3eIsx+IHJ0qJoVEOmHK3NL/iO1BGh1fm0LJXhxH
2ItvhNahfhQYUb3i0j5s9kDkXf1OPoA5TqbYDVjEPTk0R7DKA82/CeEpXAbvrNHZ
6tlhzjPW6Bp35NsCIBPtergdt3qT6GI3sfM2BlLMIfIDayM17K+4vSBrRn+uxUGA
ljl32S3hoMtENP69ZcEpssFJDWmSLIhmR73mbC7VvdzFjBcNLp3hLx7LkFfnvLHS
yP+t90xnAgMBAAECggEAPYnPFhKRRuK2WVLH+/Akop6Vae+l0hbCQMmr2/EygYgB
5bMpzYW29L1W4jw+F5RD4W3hWVpYyY5wN8jqnQXWYA8N9QyO02/VJRPBvNtXQYaf
gs80kFixucdxjVfU5i3J6nR8b9D/BIpw4jKAvtkpSEFxmo1CqyimVw5zFxw8m599
gJX/WeA88l9/4/tGQ24TAZV7OaP+jgqb4hOPC6gB5YHYnGFfuAh8q1Gf3wGnU+3/
pdEDq1UPvMwZ3J7ifTjMHYh2gnT/xQSxOddbtNJfaBx8fUFC4GVEEZ1+j0zc2bOp
/7q+Ab0QXLjMbe3bftMZZqffp2X6NLJipLQcw/HRQQKBgQDonHdqt5ZcsF7+Vsl4
KwmMNAz+jO6j51LU60F7QDhk7hGkCvUF2zJgYSkjlUNl6VS5aGmWTyUR3T3Eqiqs
r81Qao5mxF0MjUU2QKgsw57YG2yASgSPdGqW0PFu1yrxLS51qLIGbp5AuZLULO+M
iTvO1SRm29q45F9f/m0NRda0TQKBgQDYIqGVFcyQvQzGPZc5iOI9we526p+MGEsa
ysRHs8wXJKCiINH2iw1bJGyRCOIZyFQwMRteC174tRnyZpsgTu6wTgaVnTHS8ZNQ
LfjAQsMbs7TItjQF88/thujP15BXzTN7HN1y5kOVCAI7EvLJlZ4jMewfj+aqv2Sb
o5ungsWtgwKBgQCgo2WIqk5JpneDt9WcikQmsc+DfzpSsK6wYeMvxbLsaMh//B0o
NS8+BftOGoeX+qJLBINejTuxcZN1nHqqFSJ59YxwBg2oXGs+wzog59trrMyqb/Nk
SmZNzu/ctvVt5uDd2mlPLddWJZHzuzCXYjKObP2dlxkedIA1H9SZxPA4RQKBgDAS
29/ePmb/NcUuU+GfObtE1HaszxoJGUN3UFsmecG4Cuak6C6vVSQtoNxNnoTfkyI4
+f5cBx7IoWHSQrTX+a1LXZmPolJqGzsdTpPtBZq2yQJPzJh6V4hclpIMP3XYFZhP
nk39O5D9fAmJuGjwF4F6jCulBUh7U7RumqOSqcdjAoGBAKxCtQ0XT0Rlc6B37xTK
/fVYaVbSDISBSVYJTy5vjQi5z+bqUaQrmfeW1z+WoVTeP0ZUgcxTXJbPBVeAC8Wx
oTYfh5yTEu8FCBpBSWWsCteodBBZxXpINLuk9Ex44yxvuFhulugmYzyga+nqufV/
N5e8NEl7aISBW+PK16pnNO0e
-----END CERTIFICATE-----
`)
)

func init() {
	if err := loadRootCa(); err != nil {
		panic(err)
	}
	if err := loadRootKey(); err != nil {
		panic(err)
	}
}

var cacheMap = make(map[string]*tls.Certificate)

func GetCertificate(host string) (*tls.Certificate, error) {
	if cert, ok := cacheMap[host]; ok {
		return cert, nil
	}
	host, _, err := net.SplitHostPort(host)
	if err != nil {
		return nil, err
	}
	certByte, priByte, err := generatePem(host)
	if err != nil {
		return nil, err
	}
	certificate, err := tls.X509KeyPair(certByte, priByte)
	if err != nil {
		return nil, err
	}
	cacheMap[host] = &certificate
	return cacheMap[host], nil

}
func generatePem(host string) ([]byte, []byte, error) {
	max := new(big.Int).Lsh(big.NewInt(1), 128)   //把 1 左移 128 位，返回给 big.Int
	serialNumber, _ := rand.Int(rand.Reader, max) //返回在 [0, max) 区间均匀随机分布的一个随机值
	template := x509.Certificate{
		SerialNumber: serialNumber, // SerialNumber 是 CA 颁布的唯一序列号，在此使用一个大随机数来代表它
		Subject: pkix.Name{ //Name代表一个X.509识别名。只包含识别名的公共属性，额外的属性被忽略。
			CommonName: host,
		},
		NotBefore:      time.Now().AddDate(-1, 0, 0),
		NotAfter:       time.Now().AddDate(1, 0, 0),
		KeyUsage:       x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, //KeyUsage 与 ExtKeyUsage 用来表明该证书是用来做服务器认证的
		ExtKeyUsage:    []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},               // 密钥扩展用途的序列
		EmailAddresses: []string{"forward.nice.cp@gmail.com"},
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = []net.IP{ip}
	} else {
		template.DNSNames = []string{host}
	}

	priKey, err := generateKeyPair()
	if err != nil {
		return nil, nil, err
	}

	cer, err := x509.CreateCertificate(rand.Reader, &template, rootCa, &priKey.PublicKey, rootKey)
	if err != nil {
		return nil, nil, err
	}

	return pem.EncodeToMemory(&pem.Block{ // 证书
			Type:  "CERTIFICATE",
			Bytes: cer,
		}), pem.EncodeToMemory(&pem.Block{ // 私钥
			Type:  "RSA PRIVATE KEY",
			Bytes: x509.MarshalPKCS1PrivateKey(priKey),
		}), err
}

// 秘钥对 生成一对具有指定字位数的RSA密钥
func generateKeyPair() (*rsa.PrivateKey, error) {
	priKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, errors.Wrap(err, "密钥对生成失败")
	}

	return priKey, nil
}

// 加载根证书
func loadRootCa() error {
	p, _ := pem.Decode(_rootCa)
	var err error
	rootCa, err = x509.ParseCertificate(p.Bytes)
	if err != nil {
		return errors.Wrap(err, "CA证书解析失败")
	}

	return nil
}

// 加载根Private Key
func loadRootKey() error {
	p, _ := pem.Decode(_rootKey)
	var err error
	rootKey, err = x509.ParsePKCS8PrivateKey(p.Bytes)
	if err != nil {
		return errors.Wrap(err, "Key证书解析失败")
	}

	return err
}

// 获取证书原内容
func GetCaCert() []byte {
	return _rootCa
}

// 安装服务端证书, 添加信任跟证书至钥匙串,
func AddTrustedCert() error {
	dir, err := os.Getwd()
	if err != nil {
		return err
	}

	fileName := dir + "/caRootCert.crt"
	file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	defer os.Remove(fileName)
	defer file.Close()

	file.Write(_rootCa)

	var command string
	switch runtime.GOOS {
	case "darwin":
		command = fmt.Sprintf("sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain %s", fileName)
	case "windows":
		command = fmt.Sprintf("certutil -addstore -f \"ROOT\" %s", fileName)
	default:
		return errors.New("仅支持MaxOS/Windows系统")
	}
	return shell(command)
}

// 执行shell命令
func shell(command string) error {
	cmd := exec.Command("sh", "-c", command)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		return errors.Wrap(err, "")
	}
	return errors.Wrap(cmd.Wait(), out.String())
}
